<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .container {
            width: 200px;
            height: 360px;
            background-color: black;
            position: relative;
        }

        .activity_model {
            position: absolute;
            width: 20px;
            height: 20px;
            background-color: aquamarine;
            border: .3px solid #fff;
        }
        .fixed_model {
            position: absolute;
            width: 20px;
            height: 20px;
            background-color: #fefefe;
            border: .3px solid #333;
        }
    </style>
</head>

<body onload="init()">
    <div id="container"class="container">
        <!-- <div class="activity_model"></div> -->


    </div>
</body>
<script>
    var STEP = 20;//步长

    //分割容器
    var ROW_COUNT = 18;
    var COL_COUNT = 10;
    //创建每个模型的数据
    var MODELS = [ 
        {
            0: { 
                row: 2,
                col: 0
            },
            1: {
                row: 2,
                col: 1
            },

            2: {
                row: 2,
                col: 2
            },

            3: {
                row: 1,
                col: 2
            }


        }
    ]
    //当前使用的模型
    var currentModel = {}
    //标记16宫格的位置 
    var currentX=0;
    var currentY=0;
    //记录块元素位置
    //k=行_列 v=块元素
    var fixedBlocks={};



    function init() {
        createModel();
        onkeyDown();
    }

    //根据模型的数据源来创建对应的块元素
    function createModel() {
        //确认当前使用模型
        currentModel = MODELS[0];
        currentX = 0;
        currentY = 0;
        //生成对应数量的块元素
        for (const key in currentModel) {
            var divEle = document.createElement("div");
            divEle.className = "activity_model";
            document.getElementById("container").appendChild(divEle);

        }
        locationBlocks();
    }
    //根据数据源定位块元素位置
    function locationBlocks(){
  checkBound();

      //1.拿到所有的块元素
      var eles=document.getElementsByClassName("activity_model");
        for(var i=0;i<eles.length;i++){
            //单个块元素
            var activityModelEle = eles[i];
            //找到对应的行和列
            var blockModel = currentModel[i];
            //每个块元素位置有两个值确认 1.16宫格所在的位置，2.块元素在16宫格的位置
            activityModelEle.style.top = (currentY+ blockModel.row) * STEP+"px";
            activityModelEle.style.left =(currentX+ blockModel.col )* STEP+"px";
        }
      //2.找到每个块元素对应的数据
      //3.  根据每个块元素对应的数据来指定块元素的位置
    }

    //监听用户键盘事件
    function onkeyDown() {
        document.onkeydown = function (event) {
            switch (event.keyCode) {
                case 38:
                    console.log("上");
                    rotate();
                    break;
                case 39:
                    console.log("右");
                    move(1, 0);
                    break;
                case 40:
                    console.log("下");
                    move(0, 1);
                    break;
                case 37:
                    console.log("左");
                    move(-1, 0);
                    break;
            }
        }
    }

    function move(x, y) {
        //控制块元素移动
        // var activityModelEle = document.getElementsByClassName('activity_model');
     
        // activityModelEle.style.top = parseInt(activityModelEle.style.top || 0) + y * STEP + 'px';
        // activityModelEle.style.left = parseInt(activityModelEle.style.left || 0) + x * STEP + 'px';
   //16宫格移动
   if(isMeet(currentX+x,currentY+y,currentModel)){
       //底部的触碰发生在移动16宫格的时候，并且此次移动是因为Y轴的变化
       if(y!==0){
           //模型之间底部的触碰
           fixedBottomModel();
       }
       return;
   }
   currentX+=x;
   currentY+=y;
   //根据16宫格的位置重新定位块元素
   locationBlocks();
   
    }
    //旋转模型
    function rotate(){
        //旋转后的行等于旋转前的列  旋转后的列=3-旋转前的行
        //遍历模型数据源
        for(var key in currentModel){
            var blockModel=currentModel[key];
            var temp = blockModel.row;
            blockModel.row = blockModel.col;
            blockModel.col=3-temp;
            locationBlocks();
        }
    }
    //控制模型只能在容器中
    function checkBound(){
        //定义模型可以活动的边界
        var leftBound = 0,
        rightBound=COL_COUNT,
        botBOund = ROW_COUNT;
        //当模型中有一个块元素超出边界之后，让16宫格后退一步
        for(var key in currentModel){
            //块元素数据
            var blockModel=currentModel[key];
            if(blockModel.col + currentX<leftBound){
                currentX++;
            }
            if(blockModel.col + currentX>=rightBound){
                currentX--;
            }
            if(blockModel.row + currentY>=botBOund){
                currentY--;
                fixedBottomModel();
            }
        }

    }
    // 把模型固定在底部
    function fixedBottomModel(){
        //1.改变模型块元素样式
         //2.让模型不可以在进行移动
        var activityModelEles = document.getElementsByClassName('activity_model');
        for(var i=activityModelEles.length-1;i>=0;i--){
            var activityModelEle = activityModelEles[i];
            activityModelEle.className = "fixed_model";
            var blockModel=currentModel[i];
            fixedBlocks[(currentY+blockModel.row)+'_'+(currentX+blockModel.col)] = activityModelEle;

        }
      
        isRemoveLine();
        //创建新的模型
        createModel();
    }
//判断模型之间的碰撞问题
//x,y表示16宫格将要移动的位置  model 表示当前模型数据源将要完成的变化
function isMeet(x,y,model){
//模型之中的触碰，在一个固定的位置已经存在一个被固定的块元素时，
//那么活动中的模型不可以再占用该位置
//判断触碰，就是在判断活动中的模型（将要移动的位置）是否已经存在被固定的模型
//如果存在返回true表示将要移动的位置会发生碰撞，否则返回false
for(var k in model){
    var blockModel=model[k];
    if(fixedBlocks[(y+blockModel.row)+'_'+(x+blockModel.col)]){
        return true;
    }
}
return false;
}

//判断一行是否被铺满
function isRemoveLine(){
    //在一行中每一列都存在块元素，那么改行就需要被清理
    for(var i=0;i<ROW_COUNT;i++){
        var flag=true;
        for(var j=0;j<COL_COUNT;j++){
            //如果当前行有一列没有数据，那么说明当前没有被铺满
            if(!fixedBlocks[i+'_'+j]){
                flag=false;
                break;
            }

        }
        if(flag){
            //改行被铺满
            removeLine(i);
           
        }
    }
}
// 清理铺满的一行
function removeLine(line){
//1.删除改行中所有块元素 2.删除所有块元素数据源
//遍历所有列
for (var i=0;i<COL_COUNT;i++){
    document.getElementById('container').removeChild(fixedBlocks[line+'_'+i]);
    fixedBlocks[line+'_'+i] = null;
}
downLine(line);
}

//被清理行行之上的块元素下落
function downLine(line){
//1.被清理行之上所有块元素的数据源所在行数加1
//2.让块元素在容器中位置下落
//3.清理
//遍历 被清理行之上的所有行
for(var i =line-1;i>=0;i--){
    for(var j=0;j<COL_COUNT;j++){
        if(!fixedBlocks[i+'_'+j]) continue;
        fixedBlocks[(i+1)+'_'+j]=fixedBlocks[i+'_'+j];
        fixedBlocks[(i+1)+'_'+j].style.top=(i+1)*STEP+'px';
        fixedBlocks[i+'_'+j] = null;
    }
}
}
</script>

</html